package deployr.core;

import static java.util.Collections.*;

import java.util.*;

public class WebAppCount {

	private Count root = new Count();
	private Count classes = new Count();
	private Count lib = new Count();
	private Count webinf = new Count();

	private final Collection<String> added;
	private final Collection<String> updated;
	private final Collection<String> removed;

	public WebAppCount(Collection<String> added) {
		this.added = added;
		this.updated = emptyList();
		this.removed = emptyList();

		count(added);
	}

	public WebAppCount(Collection<String> added, Collection<String> updated, Collection<String> removed) {
		this.added = added;
		this.updated = updated;
		this.removed = removed;

		count(added);
		count(updated);
		count(removed);
	}

	private void add(Collection<String> updated2, WebAppMatcher matcher, List<String> toResult) {
		for (String path : updated2) {
			add(path, toResult, If(path, matcher));
		}
	}

	private void add(String path, List<String> toResult, boolean t) {
		if (t) toResult.add(path);
	}

	private List<String> addedOrUpdated(WebAppMatcher matcher) {
		List<String> toResult = new LinkedList<String>();
		add(added, matcher, toResult);
		add(updated, matcher, toResult);
		return toResult;
	}

	public int classes() {
		return classes.intValue();
	}

	public int classesAddedOrUpdated() {
		return classesAddedOrUpdatedFiles().size();
	}

	public List<String> classesAddedOrUpdatedFiles() {
		return addedOrUpdated(inClasses());
	}

	private void count(Collection<String> files) {
		for (String path : files) {
			root.increment(If(path, inRoot()));
			classes.increment(If(path, inClasses()));
			webinf.increment(If(path, inWebInf(), notInClassesAndLib()));
			lib.increment(If(path, inLib()));
		}
	}

	private boolean If(String path, WebAppMatcher matcher) {
		return matcher.match(path);
	}

	private boolean If(String path, WebAppMatcher... matchers) {
		for (WebAppMatcher matcher : matchers) {
			if (!matcher.match(path)) return false;
		}
		return true;
	}

	private WebAppMatcher inClasses() {
		return new WebAppMatcher() {
			@Override
			public boolean match(String path) {
				return path.matches("^WEB-INF/classes.*$");
			}
		};
	}

	private WebAppMatcher inLib() {
		return new WebAppMatcher() {
			@Override
			public boolean match(String path) {
				return path.matches("^WEB-INF/lib$") || path.matches("^WEB-INF/lib/.*$");
			}
		};
	}

	private WebAppMatcher inRoot() {
		return new WebAppMatcher() {
			@Override
			public boolean match(String path) {
				if (inClasses().match(path)) return false;
				if (inLib().match(path)) return false;
				if (inWebInf().match(path)) return false;
				return true;
			}
		};
	}

	private WebAppMatcher inWebInf() {
		return new WebAppMatcher() {
			@Override
			public boolean match(String path) {
				return path.matches("^WEB-INF$") || path.matches("^WEB-INF/.*$");
			}
		};
	}

	private WebAppMatcher inWebInfButNotInClassesAndLib() {
		return new WebAppMatcher() {
			@Override
			public boolean match(String path) {
				return inWebInf().match(path) && notInClassesAndLib().match(path);
			}
		};
	}

	public int lib() {
		return lib.intValue();
	}

	public int libAddedOrUpdated() {
		return libAddedOrUpdatedFiles().size();
	}

	public List<String> libAddedOrUpdatedFiles() {
		return addedOrUpdated(inLib());
	}

	private WebAppMatcher notInClassesAndLib() {
		return new WebAppMatcher() {
			@Override
			public boolean match(String path) {
				if (inClasses().match(path)) return false;
				if (inLib().match(path)) return false;
				return true;
			}
		};
	}

	public int root() {
		return root.intValue();
	}

	public int rootAddedOrUpdated() {
		return rootAddedOrUpdatedFiles().size();
	}

	public List<String> rootAddedOrUpdatedFiles() {
		return addedOrUpdated(inRoot());
	}

	public int webinf() {
		return webinf.intValue();
	}

	public int webinfAddedOrUpdated() {
		return webinfAddedOrUpdatedFiles().size();
	}

	public List<String> webinfAddedOrUpdatedFiles() {
		return addedOrUpdated(inWebInfButNotInClassesAndLib());
	}

	public int any() {
		return root.sum(classes).sum(lib).sum(webinf).intValue();
	}
}
